/***************************************************************************
 *   Copyright (C) 2008 by Chris Bornholdt   *
 *   chris.bornholdt@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifndef _CUSTOM_SIGNAL_MAPPER_HPP_
#define _CUSTOM_SIGNAL_MAPPER_HPP_

#include <QObject>
#include <QMultiMap>
#include <QMetaObject>
#include <QVariant>
#include <QtDebug>

/*! 	\class 
	\brief CustomSignalMapper extends QSignalMapper to arbitrary mapped types

	The CustomSignalMapper class extends QSignalMapper to arbitrary mapped types.
	
	Similar to QSignalMapper, this class collects parameterless signals and connects them
	to a specified slot with a given data item.  Unlike QSignalMapper, CustomSignalMapper does
	not emit a signal, but instead connects to the desired slot using QMetaObject::invokeMethod
*/
class CustomSignalMapper : public QObject
{
	Q_OBJECT

	public:
		enum WarningLevel { NoWarning, Warning, Debug };
	
		//! Constructs CustomSignalMapper with parent \e parent
		CustomSignalMapper(QObject *parent = 0, WarningLevel warningLevel = NoWarning) 
			: QObject(parent), warningLevel_(warningLevel) {}
		
		//! Destructor
		~CustomSignalMapper() {}
		
		template<class T>
		void addMapping(QObject *sender, QObject *reciever, const char *slot, T arg)
		{
			mappings_.insert(sender, MappingData(reciever, slot, QVariant::fromValue(arg)));
			connect(sender, SIGNAL(destroyed(QObject *)), this, SLOT(removeMappings(QObject *)));
		}
		
		void addMapping(QObject *sender, QObject *reciever, const char *slot)
		{
			mappings_.insert(sender, MappingData(reciever, slot, QVariant()));
			connect(sender, SIGNAL(destroyed(QObject *)), this, SLOT(removeMappings(QObject *)));
		}
		
	public slots:
		//! This slot clears all mapped entries corresponding to object \e sender
		/** detailed */
		void removeMappings(QObject *sender)
		{
			mappings_.remove(sender);
			sender->disconnect(this);
		}
		
		//! This slot clears all mapped entries
		/** detailed */
		void clear()
		{
			mappings_.clear();
		}
		
		//! This slot invokes another slot based on the \e sender object
		/** detailed */
		void map()
		{
			QObject *s = sender();
			QList<MappingData> connectionList = mappings_.values(s);
				
			QList<MappingData>::const_iterator iter = connectionList.begin();
			for(QGenericArgument arg; iter != connectionList.end(); ++iter)
			{
				arg = iter->data.isValid() ? QGenericArgument(iter->data.typeName(), 
					iter->data.data()) : QGenericArgument();
				if (!QMetaObject::invokeMethod(iter->reciever, iter->slot, arg))
				{
					switch(warningLevel_)
					{
						case NoWarning	:	return;
						case Warning	:
							qWarning() << "failed to connect to " << iter->slot;
							break;
						case Debug		:
							qDebug() << "failed to connect to " << iter->slot;
					};
				}
			}
		}
		
	private:
		struct MappingData
		{
			QObject *reciever;
			const char *slot;
			QVariant data;
			MappingData(QObject* r, const char *s, QVariant d) 
				: reciever(r), slot(s), data(d) {}
		};	
		
		WarningLevel warningLevel_;
		
		QMultiMap<QObject *, MappingData> mappings_;
};

#endif
